import React, { useEffect, useState, useRef, CSSProperties, useImperativeHandle } from "react"
import type { PropsWithChildren } from "react"
import { useThrottleFn } from "ahooks"
import styles from "./style.module.css"

export interface CarouselState {
  width: number
  height: number
  autoplay: boolean
  indicate: boolean
  control: boolean
  delay: number
  style: CSSProperties
}

export interface CarouselRef {
  goTo: (current: number) => void
  next: () => void
  prev: () => void
}

export const Carousel = React.forwardRef<CarouselRef, PropsWithChildren<Partial<CarouselState>>>((props, ref) => {
  const { width = 465, height = 160, delay = 3000, indicate = true, control = true, style = {}, autoplay = false, children } = props
  const [activeIndex, setActiveIndex] = useState(0)
  const timeoutRef = useRef<NodeJS.Timeout>()
  const THROTTLE_AWAIT = 300
  const carouselRef = useRef<HTMLDivElement>(null)
  const [transitionDuration, setTransitionDuration] = useState("300ms")
  const gotoTimer = useRef<NodeJS.Timeout>()
  useImperativeHandle(
    ref,
    () => ({
      next: () => {
        next()
      },
      prev: () => {
        prev()
      },
      goTo: (current: number) => {
        goTo(current)
      }
    }),
    [carouselRef.current]
  )

  const { run: prev } = useThrottleFn(
    () => {
      prevFn()
    },
    { wait: THROTTLE_AWAIT }
  )

  const prevFn = () => {
    setActiveIndex(prev => {
      if (prev - 1 < 0) {
        return React.Children.count(children) - 1
      } else {
        return prev - 1
      }
    })
    resetTimer()
  }

  const { run: goTo } = useThrottleFn(
    (current: number) => {
      gotoTimer.current && clearInterval((gotoTimer.current))
      const num = activeIndex - current
      let i = 0
      if (num === 0) return
      setTransitionDuration("200ms")
      if (num > 0) {
        gotoTimer.current = setInterval(() => {
          i++
          prevFn()
          if (i === Math.abs(num)) {
            setTransitionDuration("300ms")
            clearInterval(gotoTimer.current)
          }
        }, 200)
      } else if (num < 0) {
        gotoTimer.current = setInterval(() => {
          i++
          nextFn()
          if (i === Math.abs(num)) {
            setTransitionDuration("300ms")
            clearInterval(gotoTimer.current)
          }
        }, 200)
      }
    },
    {
      wait: THROTTLE_AWAIT
    }
  )

  const { run: next } = useThrottleFn(
    () => {
      nextFn()
    },
    { wait: THROTTLE_AWAIT }
  )
  const nextFn = () => {
    setActiveIndex(prev => {
      if (prev + 1 > React.Children.count(children) - 1) {
        return 0
      } else {
        return prev + 1
      }
    })
    resetTimer()
  }

  useEffect(() => {
    autoplay && startTimer()

    return () => {
      timeoutRef.current && clearInterval(timeoutRef.current)
      gotoTimer.current && clearInterval(gotoTimer.current)
    }
  }, [])

  const startTimer = () => {
    timeoutRef.current = setInterval(() => {
      setActiveIndex(prev => {
        if (prev + 1 > React.Children.count(children) - 1) {
          return 0
        } else {
          return prev + 1
        }
      })
    }, delay)
  }

  const stopTimer = () => {
    timeoutRef.current && clearInterval(timeoutRef.current)
  }
  const resetTimer = () => {
    stopTimer()
    autoplay && startTimer()
  }

  return (
    <div ref={carouselRef} className={styles.carousel} style={{ width: width, height: height, ...style }}>
      {React.Children.map(children, (ele, index) => {
        let style: any = {
          left: 0,
          zIndex: 0
        }
        let prevIndex = activeIndex - 1
        let nextIndex = activeIndex + 1

        if (prevIndex < 0) {
          prevIndex = React.Children.count(children) - 1
        } else if (nextIndex > React.Children.count(children) - 1) {
          nextIndex = 0
        }
        if (prevIndex === index) {
          style.left = -width + "px"
          style.zIndex = 1
        } else if (nextIndex === index) {
          style.left = width + "px"
          style.zIndex = 1
        } else if (activeIndex === index) {
          style.left = 0
          style.zIndex = 2
        }
        return (
          <div className={styles["carousel-item"]} style={{ ...style, transitionDuration }}>
            {React.cloneElement(ele as any, {})}
          </div>
        )
      })}
      {indicate && (
        <ul className={styles["indicate-wrapper"]}>
          {React.Children.map(children, (...val) => {
            return (
              <li key={val[1]} className={[styles["indicate-item"], activeIndex === val[1] ? styles["indicate-item-active"] : ""].join(" ")}>
                <button onClick={() => goTo(val[1])} className={styles["indicate-item-btn"]}></button>
              </li>
            )
          })}
        </ul>
      )}
      {control && (
        <ul className={styles.control}>
          <li onClick={() => prev()} className={[styles["control-item"], styles["control-left"]].join(" ")}>
            <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2669" width="20" height="20">
              <path
                d="M641.28 278.613333l-45.226667-45.226666-278.634666 278.762666 278.613333 278.485334 45.248-45.269334-233.365333-233.237333z"
                p-id="2670"
                fill="#ffffff"
              ></path>
            </svg>
          </li>
          <li onClick={() => next()} className={[styles["control-item"], styles["control-right"]].join(" ")}>
            <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3601" width="20" height="20">
              <path
                d="M593.450667 512.128L360.064 278.613333l45.290667-45.226666 278.613333 278.762666L405.333333 790.613333l-45.226666-45.269333z"
                p-id="3602"
                fill="#ffffff"
              ></path>
            </svg>
          </li>
        </ul>
      )}
    </div>
  )
})
